/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: message.h
 *
 * Purpose: message type define for communication between service and client.
 *
 * Developer:
 *   wen.gu , 2021-06-15
 *
 * TODO:
 *
 ***************************************************************************/

/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __ICPP_COM_MESSAGE_H__
#define __ICPP_COM_MESSAGE_H__
#include <memory>
#include <functional>

#include "icpp/com/types.h"
#include "icpp/com/serializable.h"
#include "icpp/com/deserializable.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace icpp
{
namespace com
{

enum class MessageFlags: uint16_t
{
    kDefualt = 0, /** default value for flags */
    kTimeSensitive = 0x0001, /** flags bit0: enable/disable time sensitive communication */
    kE2EProtect = 0x0002, /** flags bit1: enable/disable E2E protect for communication */
};

enum class MessageType: uint8_t
{
    kRequest = 0x00, /** A request expecting a response (even void) (proxy -> service)*/
    kRequestNoReturn = 0x01, /** A fire&forget request (service -> proxy) */
    kNotification = 0x02, /** A request of a notification/event callback expecting no response(service - > proxy) */
    kResponse = 0x80, /** The response message (service -> proxy) */
    //kError = 0x81, /** The response containing an error (service -> proxy) */     

    kSubscribe = 0x21, /** proxy subscirbe a event from service (proxy -> service) */
    kSubscribeAck = 0x22, /** return the subscribe state (service -> proxy) */
    kUnsubscribe = 0x23, /** unsubscribe a event (proxy -> service) */
    kUnsubscribeAck = 0x24, /** return unsubscribe state (service -> proxy */

    kPropertyGet = 0x11,     /** get property value from service (proxy -> service) */
    kPropertyGetAck = 0x12,  /** return got value from service property (service - > proxy) */
    kPropertySet = 0x13,    /** set a value to service property (proxy -> service) */
    kPropertySetAck = 0x14, /** return the new value after property set (service - > proxy) */
    kPropertyNotify = 0x15, /** notify property changed value (service -> proxy) */ 
    kPropertySubcribe = 0x16, /** subcribe the notify of property (proxy -> service)*/
    kPropertySubscribeAck = 0x17, /** return ack for subcribe the notify of property (service -> proxy)*/
    kPropertyUnsubscribe = 0x18, /** unsubscribe a property notify (proxy -> service) */
    kPropertyUnsubscribeAck = 0x19, /** return unsubscribe state (service -> proxy */   
    kHeartbeat = 0xFF,              /** the heartbeat message (service -> proxy) */
    kHearbeatAck = 0xFE,            /** the ack for heartbeat message(procxy -> service) */
};




/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/

/** a opaque parameter for message */
class MessageOpaque
{
public:
    using MessageOpaquePtr = std::shared_ptr<MessageOpaque>;
public:
    virtual ~MessageOpaque(){/**todo something */ }
};

/**
 * the structure of communication message
 * |message id(4 byte)|session id(2 byte)|flag(2 byte)|protocol version(1byte)|interface version(1byte)|type(1byte)|code(1byte)|length(4 byte)|payload(variable length)|[option]checksum(crc16 2byte)|
 * 
 * message id: the id of current message, include: method,event, property id,
 * session id:  the id of session for current message
 * flag: the flag for communication protocol
 * protocol version:  the version of current communication protocol
 * interface version:  the version of service provide interface
 * type:  the type of current message, see also: MessageType
 * code: the code of a message(maybe a return code, or event value)
 * length: the length of payload + [option]checksum
 * payload: the content of message
 * checksum[option]: a value of crc16, if the bit0 of flag is set as 1 and the payload not null, then this option will be enable.
 */
class COM_CLASS Message: public Seriablizable, public Deserializable, public std::enable_shared_from_this<Message>
{
public:
    using IcppErrc = core::IcppErrc;
    /**define smart pointer type for Message */
    using MessagePtr = std::shared_ptr<Message>;

    /** a handler to create a new message */
    using NewMessageHandler = std::function<Message::MessagePtr(MessageType, MessageId, PayloadPtr)>;
public:
    Message(MessageType type = MessageType::kRequest); /** default as a kRequest messsage */
    virtual ~Message();

public:  
    virtual MessageId    message_id() const; /** include : method, event, property */    
    virtual uint32_t     length() const; /** the length of (payload + checmsum[option])*/
    virtual SessionId    session_id() const;   
    virtual MessageFlags flag() const;
    virtual uint8_t      protocol_version() const;
    virtual uint8_t      interface_version() const;
    virtual MessageType  type() const;
    virtual uint8_t      code() const;
    virtual uint32_t     send_time() const; /** send time,in milli-second, if 0: invalid, valid condition: has kTimeSensitive flag */
    virtual uint32_t     alive_time() const; /** alive time, in milli-second, if 0: for ever, valid condition: has kTimeSensitive flag */
    virtual PayloadPtr   payload() const;
    

    virtual void set_message_id(MessageId id);
    virtual void set_session_id(SessionId id); 
    virtual void set_flag(MessageFlags flag_value);
    virtual void set_protocol_version(uint8_t version);
    virtual void set_interface_version(uint8_t version);
    virtual void set_type(MessageType type);
    virtual void set_code(uint8_t code);
    virtual void set_send_time(uint32_t send_time_ms); /**valid(serialize to message) condition: has kTimeSensitive flag */
    virtual void set_alive_time(uint32_t alive_time_ms); /**valid(serialize to message) condition: has kTimeSensitive flag */
    virtual void set_payload(PayloadPtr payload);
    
    

public:
    //virtual uint32_t header_size() const;
    virtual uint32_t payload_size()  const;
    virtual uint32_t message_size() const; /** the total size of */

public: /** for extand, will not be serialize/deserialize */
    /** used by endpoint */
    virtual void set_opaque(MessageOpaque::MessageOpaquePtr opaque_ptr);
    virtual MessageOpaque::MessageOpaquePtr opaque();

    /** used by message broker */
    virtual void set_owner(EndpointId the_owner); /** who owned current message */
    virtual EndpointId owner();
public:
    static MessagePtr make(MessageType type);
public:
    /** for serialize/deserialize */
    bool serialize(Serializer *ser_to) const override;
    bool deserialize(Deserializer *deser_from) override;

protected:
    uint32_t header_size() const;
protected:
    /** if service endian, indicate that where is the message come from */
    MessageId  message_id_ = INVALID_MESSAGE_ID;
    SessionId session_id_ = INVALID_SESSION_ID;
    MessageFlags flags_ = 0;
    uint8_t protocol_version_ = COM_PROTOCOL_VERSION;  /** default first version of current communication */
    uint8_t interface_version_ = 0x0;
    MessageType type_;    
    uint8_t code_ = 0;
    uint32_t send_time_ms_ = 0;
    uint32_t alive_time_ms_ = 0;
    EndpointId owner_ = INVALID_ENDPOINT_ID;
    PayloadPtr payload_ = nullptr;
    MessageOpaque::MessageOpaquePtr opaque_ = nullptr;
};


} /** namespace com */
} /** namespace icpp */

#endif /** !__ICPP_COM_MESSAGE_H__ */

